#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using Yarr;

namespace Yarr
{
    /// <summary>
    /// Maintains the print state of the code being pretty-printed
    /// </summary>
    public class PrintState
    {
        private const int MAX_LINE_LENTGH = 96;
        private const int DEFAULT_INDENT = 2;

        private int currentLineLength;
        private int currentIndent;
        private int maxLineLength;
        private int defaultIndent;
        private StringBuilder result = new StringBuilder();
        private Stack<int> indents = new Stack<int>();
        private Dictionary<Type, PrintMacro> dispatcher;

        private int open_parens;

        private PrintState () { }

        /// <summary>
        /// Create a PrintState instance
        /// </summary>
        /// <param name="maxLineLength">The maximum line length</param>
        /// <param name="defaultIndent">The default indent size</param>
        /// <param name="dispatcher">The print dispatcher to use</param>
        public PrintState (int maxLineLength, int defaultIndent, Dictionary<Type, PrintMacro> dispatcher)
        {
            this.maxLineLength = maxLineLength;
            this.defaultIndent = defaultIndent;
            this.dispatcher = dispatcher;
        }

        /// <summary>
        /// The length of the line currently being printed
        /// </summary>
        public int LineLength { get { return currentLineLength; } }

        /// <summary>
        /// The current indent position
        /// </summary>
        public int IndentSize { get { return currentIndent; } }

        /// <summary>
        /// The maximum line length permitted
        /// </summary>
        public int MaxLineLength { get { return maxLineLength; } }

        /// <summary>
        /// The default indent size
        /// </summary>
        public int DefaultIndent { get { return defaultIndent; } }

        /// <summary>
        /// The print dispatcher
        /// </summary>
        public Dictionary<Type, PrintMacro> Dispatcher { get { return dispatcher; } }

        /// <summary>
        /// Denotes the start of a list
        /// </summary>
        public void StartList()
        {
            ++open_parens;
        }

        /// <summary>
        /// Denotes the end of a list
        /// </summary>
        public void EndList()
        {
            currentLineLength += open_parens + 1;

            while (open_parens > 0)
            {
                result.Append("(");
                --open_parens;
            }

            result.Append(")");
        }

        /// <summary>
        /// Pushes an indent value onto the indent stack
        /// </summary>
        /// <param name="mult">The number of positions to indent, relative to the current line position</param>
        /// <returns>The computed indent position</returns>
        public int PushRelativeIndent(int mult)
        {
            int newIndent = currentLineLength + mult * defaultIndent;
            return PushAbsoluteIndent(newIndent);
        }

        /// <summary>
        /// Pushes an indent value onto the indent stack
        /// </summary>
        /// <param name="pos">The indent position</param>
        /// <returns>The indent position</returns>
        public int PushAbsoluteIndent(int pos)
        {
            if (pos > 0)
            {
                indents.Push(currentIndent);
                currentIndent = pos;
            }

            return currentIndent;
        }

        /// <summary>
        /// Pop an indent position from the indent stack
        /// </summary>
        /// <returns>The indent position</returns>
        public int PopIndent()
        {
            if (indents.Count > 0)
                currentIndent = indents.Pop();
            return currentIndent;
        }

        /// <summary>
        /// Adds the specified number of spaces to the current line
        /// </summary>
        /// <param name="size">The number of spaces to add</param>
        /// <returns>The line length after the spaces have been added</returns>
        public int Space(int size)
        {
            result.Append(String.Format(CultureInfo.CurrentCulture, "{0," + size.ToString() + "}", " "));
            currentLineLength += size;
            return currentLineLength;
        }

        /// <summary>
        /// Write text. The text will be kept together, so if it doesn't fit
        /// on the current line, it will be written on a new line. If the text
        /// is the first element of a (possibly nested) list, it will be preceded
        /// by the requisite number of left-parens.
        /// </summary>
        /// <param name="elt">The text to write</param>
        /// <returns>The length of the text that was written</returns>
        public int Write(string elt)
        {
            if (currentLineLength + elt.Length + open_parens > maxLineLength  &&
                currentIndent < currentLineLength - defaultIndent)
                    NewLine(true);

            int oldLineLen = currentLineLength;
            currentLineLength += open_parens;
            while (open_parens > 0)
            {
                result.Append("(");
                --open_parens;
            }
              
            result.Append(elt);

            currentLineLength += elt.Length;
            return currentLineLength - oldLineLen;
        }

        /// <summary>
        /// Start a new line
        /// </summary>
        /// <param name="indent"></param>
        public void NewLine(bool indent)
        {
            result.Append("\n");
            currentLineLength = 0;
            if (indent)
                Space(currentIndent);
        }

        /// <summary>
        /// Override Object.ToString()
        /// </summary>
        /// <returns>The currently formatted text</returns>
        public override string ToString ()
        {
            return result.ToString();
        }

        private bool IsWhitespace(string elt)
        {
            return String.IsNullOrEmpty(elt.Trim());
        }
    }

    public delegate void PrintMacro (Object elt, PrintState state, Yarr.Environment env);

    /// <summary>
    /// Yarr extension class - pretty printing support
    /// </summary>
    [Package("pprint")]
    public static class PrettyPrinter
    {
        private const int MAX_CONS_LENGTH = 20;

        // Package names
        private const string SYSTEM = "system";
        private const string PPRINT = "pprint";

        // Symbol names
        private const string PP_LINE_LENGTH = "*line-length*";
        private const string PP_INDENT_SIZE = "*indent-size*";
        private const string PP_DISPATCH = "*dispatch*";
        private const string PP_CONS_DISPATCH = "*cons-dispatch*";

        private const string PP_PRINT_LENGTH = "*print-length*";

        ///////////////////////
        //
        // Module initialization - these get eval'ed/bound after all functions have been imported
        //

        // Causes the Initialize method to be invoked when the class is imported
        [Eval] internal static string INIT = "(yarr.prettyprinter.initialize system:*environment*)";

        [Eval(PP_LINE_LENGTH)] internal static int LINE_LENGTH = 80;
        [Eval(PP_INDENT_SIZE)] internal static int INDENT_SIZE = 2;

        [Eval(PP_DISPATCH)]
        internal static Dictionary<Type, PrintMacro> PPRINT_DISPATCH = new Dictionary<Type, PrintMacro>();
        [Eval(PP_CONS_DISPATCH)]
        internal static Dictionary<Symbol, PrintMacro> CONS_DISPATCH = new Dictionary<Symbol, PrintMacro>();
        //
        ///////////////////////

        /// <summary>
        /// (pprint form)
        /// Pretty-prints the specified form
        /// </summary>
        /// <param name="args">A list with one element - the form to print</param>
        /// <param name="env">The local environment</param>
        /// <returns>A pretty-printed string</returns>
        [Function("user:pprint", "(form)")]
        public static Object PrettyPrint(Environment env)
        {
            Object form = env.GetValue(PPRINT, "form");

            if (form == null)
                return "null";

            Dictionary<Type, PrintMacro> dispatcher = (Dictionary<Type, PrintMacro>)env.GetValue(PPRINT, PP_DISPATCH);
            if (dispatcher == null)
                throw new YarrException("Pprint: No dispatcher bound to pprint:*dispatch*");

            PrintState result = new PrintState((int)env.GetValue(PPRINT, PP_LINE_LENGTH),
                                                (int)env.GetValue(PPRINT, PP_INDENT_SIZE),
                                                dispatcher);

            PprintDispatch(form, result, env);
            return result.ToString();
        }

        /// <summary>
        /// Pretty printer dispatcher
        /// </summary>
        /// <param name="elt">object to pretty-print</param>
        /// <param name="state">Current printer state</param>
        /// <param name="env">the local environment</param>
        public static void PprintDispatch(Object elt, PrintState state, Yarr.Environment env)
        {
            if (elt == null)
                state.Write("null");
            else
            {
                Type disp = elt.GetType();

                if (state.Dispatcher.ContainsKey(disp))
                {
                    PrintMacro printer = state.Dispatcher[disp];
                    printer(elt, state, env);
                }
                else
                    state.Write(Printer.WriteToString(elt));
            }
        }


        private static void ClosurePrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            Cons args, body;
            Type returnType = typeof(Object);
            int len;

            if (elt is Closure)
            {
                args = ((Closure)elt).Arguments;
                body = ((Closure)elt).Body;
                returnType = ((Closure)elt).ReturnType;

                state.StartList();
                len = state.Write("fn ");

            }
            else  // defun is Macro
            {
                args = ((Macro)elt).Args;
                body = ((Macro)elt).Body;
                state.StartList();
                len = state.Write("macro ");
            }

            state.PushAbsoluteIndent(state.LineLength - len + state.DefaultIndent);

            state.StartList();
            if (args != null)
            {
                if (elt is Closure)
                    ClosureArgsPrinter(args, state, env);
                else // defun is Macro
                    MacroArgsPrinter(args, state, env);
            }
            state.EndList();

            if (returnType != typeof(Object))
            {
                state.NewLine(true);
                state.Write(":returns ");

                if (returnType.IsGenericType)
                {
                    state.StartList();
                    state.Write("type ");
                    state.Write(returnType.Name.Substring(0, returnType.Name.IndexOf("`")));
                    foreach (Type t in returnType.GetGenericArguments())
                    {
                        state.Write(" ");
                        state.Write(t.Name);
                    }
                    state.EndList();
                }
                else
                    state.Write(returnType.ToString());
            }

            if (body != null)
            {
                state.NewLine(true);
                BodyPrinter(body, state, env);
            }
            else
                state.Write(" #<Internal-Function> ");

            state.PopIndent();
            state.EndList();
        }


        private static void MacroArgsPrinter (Object args, PrintState state, Yarr.Environment env)
        {
            bool haveOptional = false;
            state.PushRelativeIndent(0);
            while (args != null)
            {
                Object arg = ((Cons)args).Car();
                if (args is Cons)
                {
                    ConsPrinter(arg, state, env);
                    if (haveOptional)
                        state.NewLine(true);
                }
                else
                {
                    state.Write(arg.ToString());
                    if (arg is Symbol &&  (arg == Symbol.FromName(SYSTEM, "&key") ||
                                           arg == Symbol.FromName(SYSTEM, "&optional")))
                    {
                        if (!haveOptional)
                        {
                            state.PushRelativeIndent(0);
                            haveOptional = true;
                        }
                    }
                }

                args = ((Cons)args).Cdr();
                if (args != null)
                    state.Write(" ");
            }
            if (haveOptional)
                state.PopIndent();
            state.PopIndent();
        }


        private static void ClosureArgsPrinter (Object args, PrintState state, Yarr.Environment env)
        {
            bool haveOptional = false;
            state.PushRelativeIndent(0);
            while (args != null)
            {
                Object arg = (args is Cons) ? ((Cons)args).Car() : args;
                args = ((Cons)args).Cdr();

                if (arg is Cons)
                {
                    state.StartList();
                    ConsPrinter(((Cons)arg).Car(), state, env);
                    state.Write(" ");

                    state.PushRelativeIndent(0);
                    PprintDispatch(((Cons)arg).Cadr(), state, env);
                    state.PopIndent();
                    state.EndList();
                    if (haveOptional && args != null)
                        state.NewLine(true);
                }
                else
                {
                    state.Write(arg.ToString());
                    if (arg is Symbol && (arg == Symbol.FromName(SYSTEM, "&key") || 
                                          arg == Symbol.FromName(SYSTEM, "&optional")))
                    {
                        if (!haveOptional)
                        {
                            state.PushRelativeIndent(0);
                            haveOptional = true;
                        }
                    }
                }

                if (args != null)
                    state.Write(" ");
            }

            if (haveOptional)
                state.PopIndent();

            state.PopIndent();
        }


        private static void ConsDispatcher (Object elt, PrintState state, Yarr.Environment env)
        {
            if (elt == null)
            {
                state.StartList();
                state.EndList();
            }

            else if (elt is Symbol)
                elt = env.GetValue((Symbol)elt);

            if (elt is Cons)
            {
                Dictionary<Symbol, PrintMacro> formDispatcher = 
                    (Dictionary<Symbol, PrintMacro>)env.GetValue(PPRINT, PP_CONS_DISPATCH);
                Cons form = (Cons)elt;

                if (form.Car() is Symbol  &&  formDispatcher.ContainsKey((Symbol)form.Car()))
                    formDispatcher[(Symbol)form.Car()](form, state, env);
                else
                    ConsPrinter(elt, state, env);
            }
            else
                state.Write(elt.ToString());
        }


        private static void ConsInternalPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            int printLen = (env.GetValue(PPRINT, PP_PRINT_LENGTH) is Int32) ?
                                (int)env.GetValue(PPRINT, PP_PRINT_LENGTH) : MAX_CONS_LENGTH;
            int consLen = 0;
            while (elt != null  &&  ++consLen < printLen)
            {
                if (elt is Cons)
                {
                    PprintDispatch(((Cons)elt).Car(), state, env);

                    if (((Cons)elt).Cdr() != null)
                    {
                        if (((Cons)elt).Car() is Cons)
                            state.NewLine(true);
                        else
                            state.Write(" ");
                    }
                    elt = ((Cons)elt).Cdr();
                }
                else
                {
                    state.Write(" . ");
                    PprintDispatch(elt, state, env);
                    elt = null;
                }
            }

            if (elt != null)
                state.Write(" ... ");
        }

        private static void ConsPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            if (elt is Cons)
            {
                if (((Cons)elt).Car() == Symbol.FromName(SYSTEM, "unquote") ||
                    ((Cons)elt).Car() == Symbol.FromName(SYSTEM, "splice") ||
                    ((Cons)elt).Car() == Symbol.FromName(SYSTEM, "backquote"))
                {
                    BackquotePrinter(elt, state, env);
                    return;
                }

                state.StartList();
                if (elt != null)
                {
                    PprintDispatch(((Cons)elt).Car(), state, env);

                    elt = ((Cons)elt).Cdr();
                    if (elt != null)
                        state.Write(" ");

                    state.PushRelativeIndent(0);
                    ConsInternalPrinter(elt, state, env);
                    state.PopIndent();
                }
                state.EndList();
            }
            else
                PprintDispatch(elt, state, env);
        }

        private static void CharPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.Write(String.Format(CultureInfo.CurrentCulture, @"#\{0}", elt.ToString()));
        }

        private static void StringPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.Write(String.Format(CultureInfo.CurrentCulture, "\"{0}\"", elt.ToString()));
        }


        private static void DoPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            Cons args = (Cons)((Cons)elt).Cdr();
            state.StartList();
            state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car().ToString()));

            state.PushRelativeIndent(0);
            BodyPrinter(args, state, env);
            state.PopIndent();
            state.EndList();
        }

        private static void WhilePrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.StartList();
            int len = state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car().ToString()));
            state.PushAbsoluteIndent(state.LineLength - len + state.DefaultIndent);
            
            state.PushRelativeIndent(0);
            PprintDispatch(((Cons)elt).Cadr(), state, env);
            state.PopIndent();

            Cons body = (Cons)((Cons)elt).Cddr();
            if (body != null)
                state.NewLine(true);
            BodyPrinter(body, state, env);
            
            state.PopIndent();
            state.EndList();
        }

        private static void BodyPrinter (Cons args, PrintState state, Yarr.Environment env)
        {
            while (args != null)
            {
                PprintDispatch(args.Car(), state, env);
                args = (Cons)args.Cdr();
                if (args != null)
                    state.NewLine(true);
            }
        }


        private static void CondPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            Cons args = (Cons)((Cons)elt).Cdr();

            state.StartList();
            state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car().ToString()));
            int formIndent = state.PushRelativeIndent(1);
            
            state.PushRelativeIndent(0);         // Indent for tests
            while (args != null  &&  args.Length > 1)
            {
                PprintDispatch(args.First(), state, env);

                state.PushAbsoluteIndent(formIndent + state.DefaultIndent);
                state.NewLine(true);
                PprintDispatch(args.Second(), state, env);
                state.PopIndent();
                
                args = (Cons)args.Cddr();
                if (args != null  &&  args.Length > 1)
                    state.NewLine(true);
            }
            state.PopIndent();
            
            if (args != null)
            {
                state.NewLine(true);
                PprintDispatch(args.First(), state, env);
            }

            state.PopIndent();
            state.EndList();
        }


        private static void LambdaPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.StartList();
            int len = state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car()));
            state.PushAbsoluteIndent(state.LineLength - len + state.DefaultIndent);

            Cons args = (Cons)((Cons)elt).Cadr();
            Cons body = (Cons)((Cons)elt).Cddr();

            state.StartList();
            if (args != null)
            {
                if ((Symbol)((Cons)elt).Car() == Symbol.FromName(SYSTEM, "macro"))
                    MacroArgsPrinter(args, state, env);
                else
                    ClosureArgsPrinter(args, state, env);
            }
            state.EndList();

            if (body != null)
            {
                state.NewLine(true);
                BodyPrinter(body, state, env);
            }

            state.PopIndent();
            state.EndList();
        }


        private static void DefunPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.StartList();
            int len = state.Write(String.Format(CultureInfo.CurrentCulture, "{0} {1} ", ((Cons)elt).Car(), ((Cons)elt).Cadr()));
            state.PushAbsoluteIndent(state.LineLength - len + state.DefaultIndent);

            Cons args = (Cons)((Cons)elt).Caddr();
            Cons body = (Cons)((Cons)elt).Cdddr();

            state.StartList();
            if (args != null)
            {
                if ((Symbol)((Cons)elt).Car() == Symbol.FromName(SYSTEM, "defmacro"))
                    MacroArgsPrinter(args, state, env);
                else
                    ClosureArgsPrinter(args, state, env);
            }
            state.EndList();

            if (body != null)
            {
                state.NewLine(true);
                BodyPrinter(body, state, env);
            }

            state.PopIndent();
            state.EndList();
        }

   
        private static void LetPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.StartList();
            int len = state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car().ToString()));
            state.PushAbsoluteIndent(state.LineLength - len + state.DefaultIndent);

            Cons form = (Cons)((Cons)elt).Cdr();
            if (form != null  &&  (form.Car() is Cons))
            {
                state.StartList();

                state.PushRelativeIndent(0);
                Cons args = (Cons)form.Car();
                while (args != null)
                {
                    if (args.Car() is Cons)
                    {
                        state.StartList();
                        state.Write(args.Caar().ToString());
                        state.Write(" ");

                        state.PushRelativeIndent(0);
                        PprintDispatch(args.Cadar(), state, env);
                        state.PopIndent();

                        state.EndList();
                        if (args.Cdr() != null)
                            state.NewLine(true);
                    }
                    else
                        state.Write(args.Car().ToString());

                    args = (Cons)args.Cdr();
                }
                state.PopIndent();

                state.EndList();
                form = (Cons)form.Cdr();
            }
            else
            {
                state.Write(form.First().ToString());
                state.Write(" ");

                state.PushRelativeIndent(0);
                PprintDispatch(form.Second(), state, env);
                state.PopIndent();

                form = (Cons)form.Cddr();
            }

            state.NewLine(true);
            BodyPrinter(form, state, env);
            state.PopIndent();
            state.EndList();
        }


        private static void QuotePrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.Write("'");
            ConsPrinter(((Cons)elt).Cadr(), state, env);
        }

        private static void FunctionPrinter(Object elt, PrintState state, Yarr.Environment env)
        {
            state.Write("#'");
            Cons form = (Cons)elt;
            
            if (form.Cadr() is Cons)
            {
                Cons target = (Cons)form.Cadr();
                state.Write(String.Format(CultureInfo.CurrentCulture, "{0}.{1}", target.Second().ToString(), 
                                                     target.First().ToString()));
            }
            else
                state.Write(form.Cadr().ToString());
        }

        private static void BackquotePrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            Symbol sym = (Symbol) ((Cons)elt).Car();
            elt = ((Cons)elt).Cadr();
            state.Write((sym == Symbol.FromName(SYSTEM, "backquote")) ? "`" :
                        (sym == Symbol.FromName(SYSTEM, "unquote")) ? "," :
                        (sym == Symbol.FromName(SYSTEM, "splice")) ? ",@" : sym.Name);
            PprintDispatch(elt, state, env);
        }

        private static void SetfPrinter (Object elt, PrintState state, Yarr.Environment env)
        {
            state.StartList();
            state.Write(String.Format(CultureInfo.CurrentCulture, "{0} ", ((Cons)elt).Car().ToString()));
            state.PushRelativeIndent(0);

            Cons forms = (Cons)((Cons)elt).Cdr();

            while (forms != null)
            {
                PprintDispatch(forms.Car(), state, env);
                state.Write(" ");

                state.PushRelativeIndent(0);
                PprintDispatch(forms.Cadr(), state, env);
                state.PopIndent();

                forms = (Cons)forms.Cddr();
                if (forms != null)
                    state.NewLine(true);
            }

            state.PopIndent();
            state.EndList();
        }

        public static void Initialize (Yarr.Environment env)
        {
            PrintMacro closurePrinter = new PrintMacro(ClosurePrinter);
            PrintMacro consDispatcher = new PrintMacro(ConsDispatcher);
            PrintMacro stringPrinter = new PrintMacro(StringPrinter);
            PrintMacro charPrinter = new PrintMacro(CharPrinter);
            
            PPRINT_DISPATCH[typeof(Closure)] = closurePrinter;
            PPRINT_DISPATCH[typeof(Macro)] = closurePrinter;
            PPRINT_DISPATCH[typeof(Cons)] = consDispatcher;
            PPRINT_DISPATCH[typeof(String)] = stringPrinter;
            PPRINT_DISPATCH[typeof(Char)] = charPrinter;

            PrintMacro condPrinter = new PrintMacro(CondPrinter);
            PrintMacro letPrinter = new PrintMacro(LetPrinter);
            PrintMacro doPrinter = new PrintMacro(DoPrinter);
            PrintMacro whilePrinter = new PrintMacro(WhilePrinter);
            PrintMacro quotePrinter = new PrintMacro(QuotePrinter);
            PrintMacro backQuotePrinter = new PrintMacro(BackquotePrinter);
            PrintMacro setfPrinter = new PrintMacro(SetfPrinter);
            PrintMacro lambdaPrinter = new PrintMacro(LambdaPrinter);
            PrintMacro defunPrinter = new PrintMacro(DefunPrinter);
            PrintMacro functionPrinter = new PrintMacro(FunctionPrinter);

            CONS_DISPATCH[Symbol.FromName(SYSTEM, "do")] = doPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "while")] = whilePrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "cond")] = condPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "if")] = condPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "let")] = letPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "let*")] = letPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "to")] = letPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "each")] = letPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "foreach")] = letPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "backquote")] = backQuotePrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "splice")] = backQuotePrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "unquote")] = backQuotePrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "quote")] = quotePrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "=")] = setfPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "setq")] = setfPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "setf")] = setfPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "fn")] = lambdaPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "lambda")] = lambdaPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "macro")] = lambdaPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "defun")] = defunPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "defmacro")] = defunPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "defsetf")] = defunPrinter;
            CONS_DISPATCH[Symbol.FromName(SYSTEM, "function")] = functionPrinter;
        }
    }
}
